package com.spzx.product.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.spzx.common.core.exception.ServiceException;
import com.spzx.common.redis.cache.GuiguCache;
import com.spzx.common.security.utils.SecurityUtils;
import com.spzx.product.api.domain.*;
import com.spzx.product.api.domain.vo.SkuLockVo;
import com.spzx.product.api.domain.vo.SkuStockVo;
import com.spzx.product.domain.SkuStock;
import com.spzx.product.mapper.*;
import com.spzx.product.service.BrandService;
import com.spzx.product.service.CategoryService;
import com.spzx.product.service.ProductService;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RBloomFilter;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @Author GQS @Date 2024/9/20/21:15 @Description
 */
@Slf4j
@Service
public class ProductServiceImpl extends ServiceImpl<ProductMapper, Product>
    implements ProductService {

  @Autowired private BrandService brandService;
  @Autowired private CategoryService categoryService;

  @Autowired private ProductMapper productMapper;
  @Autowired private ProductSkuMapper productSkuMapper;
  @Autowired private ProductDetailsMapper productDetailsMapper;
  @Autowired private SkuStockMapper skuStockMapper;

  @Autowired private RedisTemplate redisTemplate;
  @Autowired private RedissonClient redissonClient;

  /**
   * 查询商品列表
   *
   * @param product 商品
   * @return 商品
   */
  @Override
  public List<Product> selectProductList(Product product) {
    // 通用service
    List<Product> productList =
        this.list(
            Wrappers.lambdaQuery(Product.class)
                .like(product.getName() != null, Product::getName, product.getName())
                .eq(product.getBrandId() != null, Product::getBrandId, product.getBrandId())
                .eq(
                    product.getCategory1Id() != null,
                    Product::getCategory1Id,
                    product.getCategory1Id())
                .eq(
                    product.getCategory3Id() != null,
                    Product::getCategory2Id,
                    product.getCategory2Id())
                .eq(
                    product.getCategory3Id() != null,
                    Product::getCategory3Id,
                    product.getCategory3Id()));
    for (Product oneProduct : productList) {
      // 查询品牌名称
      Long brandId = oneProduct.getBrandId();
      oneProduct.setBrandName(brandService.selectBrandById(brandId).getName());
      // product里包含了各级分类Id 不需要查找
      // 先根据brandId通过中间表查询categoryId
      // Long categoryId=categoryBrandMapper.selectCategoryIdByBrandId(brandId);
      // 再根据三级分类Id查询所有分类Id的列表
      // List<Long> categoryIdList = categoryService.getAllCategoryIdList(categoryId);
      // oneProduct.setCategory1Id(categoryIdList.get(0));
      // oneProduct.setCategory2Id(categoryIdList.get(1));
      // oneProduct.setCategory3Id(categoryIdList.get(2));

      // 查询分类名称
      oneProduct.setCategory1Name(categoryService.getById(oneProduct.getCategory1Id()).getName());
      oneProduct.setCategory2Name(categoryService.getById(oneProduct.getCategory2Id()).getName());
      oneProduct.setCategory3Name(categoryService.getById(oneProduct.getCategory3Id()).getName());
    }
    return productList;

    // 自定义SQL
    // return productMapper.selectProductList(product);
  }

  /**
   * 新增商品
   *
   * @param product
   * @return
   */
  @Transactional(rollbackFor = Exception.class)
  @Override
  public int insertProduct(Product product) {
    // 1.插入商品基本信息
    // 设置创建人
    product.setCreateBy(SecurityUtils.getUsername());
    // 设置审核信息
    // product.setAuditMessage("审核通过");
    int flag = productMapper.insert(product);
    // 2.插入商品详情信息
    ProductDetails productDetails = new ProductDetails();
    // System.out.println(product.getId());
    productDetails.setProductId(product.getId());
    // 将图片URL列表转化为用逗号拼接的字符串
    // String.join工具
    productDetails.setImageUrls(String.join(",", product.getDetailsImageUrlList()));
    // stream 流式处理
    // productDetails.setImageUrls(product.getDetailsImageUrlList().stream()
    //             .map(url -> url + ",")
    //             .reduce((a, b) -> a + b)
    //             .map(s -> s.isEmpty() ? "" : s.substring(0, s.length() - 1)) // 去除末尾逗号
    //             .orElse(""));
    productDetailsMapper.insert(productDetails);
    // 3.遍历插入商品的sku列表
    List<ProductSku> productSkuList = product.getProductSkuList();
    int size = productSkuList.size();
    for (int i = 0; i < size; i++) {
      ProductSku productSku = productSkuList.get(i);
      productSku.setProductId(product.getId());
      // SkuCode = productId + "_" + 序号下标(从0开始)
      productSku.setSkuCode(product.getId() + "_" + i);
      productSku.setSkuName(product.getName() + " " + productSku.getSkuSpec());
      productSkuMapper.insert(productSku);

      // 4.插入每个sku的库存信息
      SkuStock skuStock = new SkuStock();
      skuStock.setSkuId(productSku.getId());
      skuStock.setTotalNum(productSku.getStockNum());
      skuStock.setLockNum(0);
      skuStock.setAvailableNum(productSku.getStockNum());
      skuStock.setSaleNum(0);
      skuStockMapper.insert(skuStock);
    }

    // 返回商品id 非0 代表成功
    // return product.getId().intValue();
    return flag;
  }

  /**
   * 查询商品
   *
   * @param id 商品主键
   * @return 商品
   */
  @Override
  public Product selectProductById(Long id) {
    // 1.查找商品基本信息
    Product product = productMapper.selectById(id);

    // 2.根据商品Id查找商品详情
    ProductDetails productDetails =
        productDetailsMapper.selectOne(
            new LambdaQueryWrapper<ProductDetails>().eq(ProductDetails::getProductId, id));
    // 将用逗号拼接的图片URL字符串转化为list
    product.setDetailsImageUrlList(Arrays.asList(productDetails.getImageUrls().split(","))); // 逗号分割

    // 3.根据商品Id查询商品的SKU列表
    List<ProductSku> productSkuList =
        productSkuMapper.selectList(
            Wrappers.lambdaQuery(ProductSku.class).eq(ProductSku::getProductId, id));
    //
    for (ProductSku productSku : productSkuList) {
      // 4.根据skuId查询每一个商品sku的库存信息
      SkuStock skuStock =
          skuStockMapper.selectOne(
              Wrappers.lambdaQuery(SkuStock.class).eq(SkuStock::getSkuId, productSku.getId()));
      // 封装现存库存数量信息
      productSku.setStockNum(skuStock.getAvailableNum());
    }
    // 方法二 减少SQL查询次数 优化性能
    // // 从商品SKU列表中提取每个SKU的ID，生成一个skuId集合用于查询库存信息
    // List<Long> skuIdList =
    //     productSkuList.stream().map(ProductSku::getId).collect(Collectors.toList());
    //
    // // 根据SKU ID列表查询库存信息，只查询SKU ID和库存数量两列
    // List<SkuStock> skuStockList =
    //     skuStockMapper.selectList(
    //         new LambdaQueryWrapper<SkuStock>()
    //             .in(SkuStock::getSkuId, skuIdList)
    //             .select(SkuStock::getSkuId, SkuStock::getTotalNum));
    //
    // // 将SKU库存列表转换为以SKU ID为键、库存数量为值的映射关系
    // Map<Long, Integer> skuIdToStockNumMap =
    //     skuStockList.stream().collect(Collectors.toMap(SkuStock::getSkuId,
    // SkuStock::getTotalNum));
    //
    // // 遍历产品SKU列表，为每个产品SKU设置其对应的库存数量
    // productSkuList.forEach(
    //     item -> {
    //       item.setStockNum(skuIdToStockNumMap.get(item.getId()));
    //     });

    product.setProductSkuList(productSkuList);
    return product;
  }

  /**
   * 修改商品信息
   *
   * @param product 商品
   * @return
   */
  @Transactional(rollbackFor = Exception.class)
  @Override
  public int updateProduct(Product product) {
    // 1.修改商品基本信息
    int flag = productMapper.updateById(product);

    // 2.修改商品详细信息
    ProductDetails productDetails =
        productDetailsMapper.selectOne(
            new LambdaQueryWrapper<ProductDetails>()
                .eq(ProductDetails::getProductId, product.getId()));
    // 将图片URL列表转化为用逗号拼接的字符串
    productDetails.setImageUrls(String.join(",", product.getDetailsImageUrlList()));
    productDetailsMapper.updateById(productDetails);
    // 3.遍历修改商品SKU列表
    List<ProductSku> productSkuList = product.getProductSkuList();
    productSkuList.forEach(
        productSku -> {
          // 修改商品SKU信息
          productSkuMapper.updateById(productSku);

          // 修改商品库存
          SkuStock skuStock =
              skuStockMapper.selectOne(
                  new LambdaQueryWrapper<SkuStock>().eq(SkuStock::getSkuId, productSku.getId()));
          // 设置总库存
          skuStock.setTotalNum(productSku.getStockNum());
          // 设置可用库存
          int availableNum = skuStock.getTotalNum() - skuStock.getLockNum();
          skuStock.setAvailableNum(availableNum);

          skuStockMapper.updateById(skuStock);
        });
    // 修改完db中的商品信息
    // todo 把修改后的数据同步至redis
    // 遍历productSkuList列表 将每个sku信息同步到redis中
    for (ProductSku productSku : productSkuList) {
      String dataKey = "product:sku:" + productSku.getId();// 数据key
      redisTemplate.opsForValue().set(dataKey, productSku);
    }


    return flag;
  }

  /**
   * 批量删除商品
   *
   * @param ids 需要删除的商品主键
   * @return 结果
   */
  @Transactional(rollbackFor = Exception.class)
  @Override
  public int deleteProductByIds(Long[] ids) {
    // 方法一
    // 1.根据商品Id(主键)List批量删除商品基本信息
    // int flag = productMapper.deleteBatchIds(Arrays.asList(ids));
    // // 2.删除商品详情列表
    // productDetailsMapper.delete(
    //         Wrappers.lambdaQuery(ProductDetails.class).in(ProductDetails::getProductId, ids));
    // // 3.获取sku列表
    // List<ProductSku> productSkuList =
    //     productSkuMapper.selectList(
    //         new LambdaQueryWrapper<ProductSku>()
    //             .in(ProductSku::getProductId, ids)
    //             .select(ProductSku::getId)); // 只查找Id
    // // 4.获取skuId列表
    // List<Long> skuIdList =
    //     productSkuList.stream().map(ProductSku::getId).collect(Collectors.toList());
    // // 5.删除sku列表
    // productSkuMapper.delete(new LambdaQueryWrapper<ProductSku>().in(ProductSku::getProductId,
    // ids));
    // // 6.删除sku库存列表
    // skuStockMapper.delete(new LambdaQueryWrapper<SkuStock>().in(SkuStock::getSkuId, skuIdList));

    // 方法二 遍历sku列表删除sku和skuStock
    // 1.根据商品Id(主键)List批量删除商品基本信息
    int flag = productMapper.deleteBatchIds(Arrays.asList(ids));
    // 遍历商品Id列表
    for (Long productId : ids) {
      // 2.删除商品详细信息
      productDetailsMapper.delete(
          Wrappers.lambdaQuery(ProductDetails.class).eq(productId!=null, ProductDetails::getProductId, productId));
      // 3.根据商品Id(主键)获取商品sku信息
      ProductSku productSku = productSkuMapper.selectById(productId);
      // 4.删除商品sku的库存信息
      skuStockMapper.delete(
          new LambdaQueryWrapper<SkuStock>().eq(productSku.getId()!=null, SkuStock::getSkuId, productSku.getId()));
      // 5.删除商品sku
      productSkuMapper.deleteById(productSku);
    }

    return flag;
  }

  /**
   * 商品审核
   *
   * @param id
   * @param auditStatus
   * @return
   */
  @Transactional(rollbackFor = Exception.class)
  @Override
  public void updateAuditStatus(Long id, Integer auditStatus) {
    Product product = new Product();
    product.setId(id);
    if (auditStatus == 1) {
      product.setAuditStatus(1);
      product.setAuditMessage("审批通过");
    } else {
      product.setAuditStatus(-1);
      product.setAuditMessage("审批不通过");
    }
    productMapper.updateById(product);
  }

  /**
   * 商品审核
   *
   * @param id
   * @param status
   * @return
   */
  @Transactional(rollbackFor = Exception.class)
  @Override
  /**
   * 更新产品的状态
   *
   * @param id 产品的ID
   * @param status 产品的状态（1：上架，-1：下架）
   */
  public void updateStatus(Long id, Integer status) {
      // 创建Product对象并设置ID，准备更新其状态
      Product product = new Product();
      product.setId(id);

      // 根据传入的状态参数更新产品状态
      if(status == 1) {
        // 如果状态为1，表示上架产品
        product.setStatus(1);

        // sku加入布隆过滤器，用于快速查询是否存在
        RBloomFilter<Object> bloomFilter = redissonClient.getBloomFilter("sku:bloom:filter");
        // 查询该产品所有的SKU
        List<ProductSku> productSkuList = productSkuMapper.selectList(new LambdaQueryWrapper<ProductSku>().eq(ProductSku::getProductId, id));
        // 将每个SKU的ID加入布隆过滤器
        productSkuList.forEach(item -> {
          bloomFilter.add(item.getId());
        });
      } else {
        // 如果状态不为1，表示下架产品
        product.setStatus(-1);
      }
      // 更新产品的状态
      productMapper.updateById(product);
  }

    @Override
    public List<ProductSku> getTopSale() {
      return productSkuMapper.selectTopSale();
    }

  @Override
  public List<ProductSku> selectProductSkuList(SkuQuery skuQuery) {
    return productSkuMapper.selectProductSkuList(skuQuery);
  }

  /**
   * 根据SkuID查询SKU商品信息
   *
   * @param skuId
   * @return
   */
  @Override
  public ProductSku getProductSku(Long skuId) {
    try {
      //1.优先从缓存中获取数据
      //1.1 构建业务数据Key 形式：前缀+业务唯一标识
      String dataKey = "product:sku:" + skuId;
      //1.2 查询Redis获取业务数据
      ProductSku productSku = (ProductSku) redisTemplate.opsForValue().get(dataKey);
      //1.3 命中缓存则直接返回
      if (productSku != null) {
        log.info("命中缓存，直接返回，线程ID：{}，线程名称：{}", Thread.currentThread().getId(), Thread.currentThread().getName());
        return productSku;
      }
      //2.尝试获取分布式锁（set k v ex nx可能获取锁失败）
      //2.1 构建锁key
      String lockKey = "product:sku:lock:" + skuId;
      //2.2 采用UUID作为线程标识
      String lockVal = UUID.randomUUID().toString().replaceAll("-", "");
      //2.3 利用Redis提供set nx ex 获取分布式锁
      // 尝试在Redis中设置一个键值对，如果键不存在，则设置成功，返回true；如果键已存在，则设置失败，返回false
      // 这里用于实现分布式锁，lockKey为锁的唯一标识，lockVal为锁的值，5为锁的超时时间（秒），TimeUnit.SECONDS为时间单位
      Boolean flag = redisTemplate.opsForValue().setIfAbsent(lockKey, lockVal, 5, TimeUnit.SECONDS);
      //尝试获取分布式锁
      if (flag) {
        //获取锁成功执行业务,将查询业务数据放入缓存Redis
        log.info("获取锁成功：{}，线程名称：{}", Thread.currentThread().getId(), Thread.currentThread().getName());
        try {
          //从数据库中获取商品SKU信息
          productSku = this.getProductSkuFromDB(skuId);
          //设置缓存过期时间，如果商品信息为空则为1分钟，否则为10分钟
          long ttl = productSku == null ? 1 * 60 : 10 * 60;
          //将商品SKU信息存入Redis缓存
          redisTemplate.opsForValue().set(dataKey, productSku, ttl, TimeUnit.SECONDS);
          //返回商品SKU信息
          return productSku;
        } finally {
          // 业务执行完毕释放锁
          // lua 脚本
          // 检查Redis中指定键的值是否与给定值相等。
          // 如果相等，则删除该键。
          // 否则返回0。
          String scriptText =
              "if redis.call(\"get\",KEYS[1]) == ARGV[1]\n"
                  + "then\n"
                  + "    return redis.call(\"del\",KEYS[1])\n"
                  + "else\n"
                  + "    return 0\n"
                  + "end";
          //定义Redis释放锁的脚本
          DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
          redisScript.setScriptText(scriptText);
          redisScript.setResultType(Long.class);
          //执行Redis脚本释放锁
          redisTemplate.execute(redisScript, Arrays.asList(lockKey), lockVal);
        }
      } else {
        //获取锁失败则自旋（业务要求必须执行）
        try {
          Thread.sleep(200);
        } catch (InterruptedException e) {
          throw new RuntimeException(e);
        }
        log.error("获取锁失败，自旋：{}，线程名称：{}", Thread.currentThread().getId(), Thread.currentThread().getName());
        //递归尝试再次获取商品SKU信息
        return this.getProductSku(skuId);
      }

    } catch (Exception e) {
      //兜底处理方案：Redis服务有问题，将业务数据获取自动从数据库获取
      log.error("[商品服务]查询商品信息异常：{}", e);
      return this.getProductSkuFromDB(skuId);
    }
  }

  /**
   * 从数据库中获取商品信息
   *
   * @param skuId
   * @return
   */
  public ProductSku getProductSkuFromDB(Long skuId) {
    return productSkuMapper.selectById(skuId);
  }

  @GuiguCache(prefix = "product:")
  @Override
  public Product getProduct(Long id) {
    return productMapper.selectById(id);
  }

  @GuiguCache(prefix = "SkuPrice:")
  @Override
  public SkuPrice getSkuPrice(Long skuId) {
    ProductSku productSku = productSkuMapper.selectOne(Wrappers.lambdaQuery(ProductSku.class)
            .eq(ProductSku::getId, skuId)
            .select(ProductSku::getSalePrice, ProductSku::getMarketPrice));
    SkuPrice skuPrice = new SkuPrice();
    BeanUtils.copyProperties(productSku, skuPrice);
    return skuPrice;
  }

  @GuiguCache(prefix = "productDetails:")
  @Override
  public ProductDetails getProductDetails(Long id) {
    // productDetailsMapper.selectOne(new QueryWrapper<ProductDetails>()
    //         .eq("ProductId", id));
    return productDetailsMapper.selectOne(new LambdaQueryWrapper<ProductDetails>()
            .eq(ProductDetails::getProductId, id));
  }

  @GuiguCache(prefix = "skuSpecValue:")
  @Override
  public Map<String, Long> getSkuSpecValue(Long id) {
    List<ProductSku> productSkuList = productSkuMapper.selectList(new LambdaQueryWrapper<ProductSku>()
            .eq(ProductSku::getProductId, id)
            .select(ProductSku::getId, ProductSku::getSkuSpec));
    Map<String,Long> skuSpecValueMap = new HashMap<>();
    productSkuList.forEach(item -> {
      skuSpecValueMap.put(item.getSkuSpec(), item.getId());
    });
    return skuSpecValueMap;
  }

  @GuiguCache(prefix = "skuStock:")
  @Override
  public SkuStockVo getSkuStock(Long skuId) {
    SkuStock skuStock = skuStockMapper.selectOne(new LambdaQueryWrapper<SkuStock>().eq(SkuStock::getSkuId, skuId));
    SkuStockVo skuStockVo = new SkuStockVo();
    BeanUtils.copyProperties(skuStock, skuStockVo);
    return skuStockVo;
  }

    @Override
    public List<SkuPrice> getSkuPriceList(List<Long> skuIdList) {
    // 从数据库中查询商品SKU信息
      List<ProductSku> productSkuList = productSkuMapper.selectList(new LambdaQueryWrapper<>(ProductSku.class)
              .in(ProductSku::getId, skuIdList)
              .select(ProductSku::getId, ProductSku::getSalePrice));
      // 转换为SkuPrice列表
      List<SkuPrice> skuPriceList = productSkuList.stream().map(productSku -> {
        SkuPrice skuPrice = new SkuPrice();
        skuPrice.setSkuId(productSku.getId());
        skuPrice.setSalePrice(productSku.getSalePrice());
        return skuPrice;
      }).toList();
      return skuPriceList;
    }

  /**
   * 检查并锁定库存
   * @param orderNo
   * @param skuLockVoList
   * @return String
   */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public String checkAndLock(String orderNo, List<SkuLockVo> skuLockVoList) {
      String lockKey = "sku:checkAndLock:" + orderNo;
      String dataKey = "sku:lock:data" + orderNo;
      // 防止重复发送请求 检查key是否存在
      boolean isExist = redisTemplate.opsForValue().setIfAbsent(lockKey, orderNo, 1, TimeUnit.HOURS);
      if (!isExist){
        // todo 我不明白
        // 如果 key 已存在且 dataKey 也存在，说明已执行过库存锁定 ，则返回空字符串
        if (redisTemplate.hasKey(dataKey)){
          return "";
        } else {
          // 如果 key 已存在但 dataKey 不存在，则返回 "重复请求"
          return "重复请求";
        }
      }
      // 遍历所有的商品，验证库存并锁定库存， 该操作要具备原子性
      skuLockVoList.forEach(skuLockVo -> {
        // 验证库存：查询， 返回的是满足要求的库存列表
        SkuStock skuStock =skuStockMapper.checkStock(skuLockVo.getSkuId(), skuLockVo.getSkuNum());
        // 如果没有一个商品满足要求，这里就验证库存失败
          skuLockVo.setIsHaveStock(skuStock != null);
      });

      // 只要有一个商品锁定库存失败，所有锁定成功的商品就要解锁库存
      if (skuLockVoList.stream().anyMatch(skuLockVo -> !skuLockVo.getIsHaveStock())){
        // 获取所有的锁定成功的商品，遍历解锁库存
        StringBuilder result = new StringBuilder();
        // 获取没有库存的对象列表
        List<SkuLockVo> noHaveStockSkuLockVoList = skuLockVoList.stream().filter(skuLockVo -> !skuLockVo.getIsHaveStock()).toList();
        for (SkuLockVo noHaveStockSkuLockVo : noHaveStockSkuLockVoList) {
            // 解除去重
          // todo 我不明白
          this.redisTemplate.delete(lockKey);
          result.append("商品：" + noHaveStockSkuLockVo.getSkuId() + "库存不足；");
        }
        // 锁定失败，解除
        redisTemplate.delete(lockKey);
        // 响应锁定状态
        return result.toString();
      } else {
        // 锁定库存
        skuLockVoList.forEach(skuLockVo -> {
          // 锁定库存
          int row =skuStockMapper.lockStock(skuLockVo.getSkuId(), skuLockVo.getSkuNum());
          if (row == 0){
            // 解除去重
            this.redisTemplate.delete(lockKey);
            throw new ServiceException("锁定库存失败");
          }
        });
      }
      // 如果所有商品都锁定成功的情况下，需要缓存锁定消息到redis，以方便将来锁定库存或减库存
      redisTemplate.opsForValue().set(dataKey, skuLockVoList);
      return "";
    }

  @Transactional(rollbackFor = Exception.class)
  @Override
  public void unlockStock(String orderNo) {
    // 分布式锁
    String lockKey = "sku:unlock:" + orderNo;
    String dataKey = "sku:lock:data:" + orderNo;
    //业务去重，防止重复消费
    // 如果键已存在则返回 false，键不存在，则设置键并返回 true
    Boolean isExist = redisTemplate.opsForValue().setIfAbsent(lockKey, orderNo, 1, TimeUnit.HOURS);
    // 键已存在，则直接返回
    if(!isExist) return;
    // 获取锁定库存的缓存信息
    List<SkuLockVo> skuLockVoList = (List<SkuLockVo>) redisTemplate.opsForValue().get(dataKey);
    // 如果库存锁定列表为空 直接返回
    if (CollectionUtils.isEmpty(skuLockVoList)){
      return;
    }
    // 解锁库存
    skuLockVoList.forEach(skuLockVo -> {
      int row=skuStockMapper.unLockStock(skuLockVo.getSkuId(), skuLockVo.getSkuNum());
      if (row == 0){
        // 解除去重
        this.redisTemplate.delete(lockKey);
        throw new ServiceException("解锁库存失败");
      }
    });
    // 解锁库存之后， 删除锁定库存的缓存 以防止重复解锁库存
    this.redisTemplate.delete(dataKey);
  }

  @Transactional(rollbackFor = {Exception.class})
  @Override
  public void minus(String orderNo) {
    String lockKey = "sku:minus:" + orderNo;
    String dataKey = "sku:lock:data:" + orderNo;
    //业务去重，防止重复消费
    Boolean isExist = redisTemplate.opsForValue().setIfAbsent(lockKey, orderNo, 1, TimeUnit.HOURS);
    if(!isExist) return;

    // 获取锁定库存的缓存信息
    List<SkuLockVo> skuLockVoList = (List<SkuLockVo>)this.redisTemplate.opsForValue().get(dataKey);
    if (CollectionUtils.isEmpty(skuLockVoList)){
      return ;
    }
    // 减库存
    skuLockVoList.forEach(skuLockVo -> {
      int row = skuStockMapper.minusStock(skuLockVo.getSkuId(), skuLockVo.getSkuNum());
      if(row == 0) {
        //解除去重
        this.redisTemplate.delete(lockKey);
        throw new ServiceException("减出库失败");
      }
    });

    // 解锁库存之后，删除锁定库存的缓存。以防止重复解锁库存
    this.redisTemplate.delete(dataKey);
  }
}
